import cPickle as pickle

class File:
    def __init__(self, path, title):
        self.tags = {}
        self.path = path
        self.title = title

    def __str__(self):
        return '%s, %s' % (tfile.title, tfile.path)

class Tagging:
    pdf_reader_path = ''
    TOP_TAG_RATIO = 0.1
    MINI_TAG_NUMBER = 50
    SEARCH_RESEARCH_NUM = 10
    tfiles = {}#tfile[path] = the tfile at [path]
    words_to_ignore = []
    db_path = 'db.db'

    @classmethod
    def initiate(cls):
        words_to_ignore = file('500topwords.txt', 'r').read().split(',')

        cls.updateDB('read')

    @classmethod
    def __tagFile(cls, tfile):
        import re
        import nltk
        import os

        if len(tfile.tags): #skip this tfile if it has been tagged
            return
        if(tfile.title[-3:] != 'pdf'):
            return

        s = ''
        try:
            cmd = 'pdftotext.exe "%s"' % tfile.path
            print 'converting %s ...' % tfile.path
            os.popen(cmd)
            s += file(tfile.path[0:-3] + 'txt', 'r').read()
        except Exception, e:
            print 'conversion failed. error = %s, tfile: %s' % (e, tfile.path)
            pass

        porter = nltk.PorterStemmer()
        article = re.findall(r"\w+(?:[-']\w+)*|'|[-.(]+|\S\w*", s)
        tfile.tags = dict(nltk.FreqDist([porter.stem(word).lower() for word in article]).items())

    @classmethod
    def tagFolder(cls, folder):
        """
        input:
            folder: the path to a folder
        desc:
            Tags [folder]. By 'tags' a folder, it means to create tags for tfiles in the folder and the subfolders.
        """
        import os
        if not os.path.isdir(folder):
            print 'wrong path..'
            return

        for root,dirs,tfiles in os.walk(folder):
            for f in tfiles:
                path = "%s\\%s" % (root, f)
                if path not in cls.tfiles:
                    if(path[-3:] != 'pdf'):
                        continue

                    cls.tfiles[path] = tfile = File(path = path, title = f)
                    cls.__tagFile(tfile)

        cls.updateDB()
        
    @classmethod
    def updateDB(cls, cmd = 'save'):
        if cmd == 'read':
            f = 0
            try:
                f = file(cls.db_path, 'r')
            except:
                print 'open db failed'
                pass
            
            if f:
                tagging = pickle.load(f)
                cls.tfiles = tagging.setdefault('tfiles', {})
                cls.pdf_reader_path = tagging.setdefault('pdf_reader_path', '')
                f.close()
        
        else:
            f = file(cls.db_path, 'w') 
            paras = dict(
                    pdf_reader_path = cls.pdf_reader_path,
                    tfiles = cls.tfiles
                    )
            
            pickle.dump(paras, f)
            f.close

    @classmethod
    def searchTags(cls, tags):
        """
        input:
            tags: a string containing the tags to search for
        desc:
            compute the weight of each file for [tags], and return the files ordered by their weights decendantly
        """
        import nltk

        for tfile in cls.tfiles.values():
            tfile.weight = 0

        poter = nltk.PorterStemmer()
        tags = [poter.stem(tag).lower() for tag in tags.split(',')]
        for tfile in cls.tfiles.values():
            for tag in tags:
                tfile.weight += tfile.tags.get(tag, 0)

        ret = sorted(cls.tfiles.values(), key = lambda tfile:tfile.weight, reverse = True)
        return ret[:min(ret, cls.SEARCH_RESEARCH_NUM)]

    @classmethod
    def displayFiles(cls, tags, tfiles):
        """
        tags: a string containing the tags to show
        """
        import nltk

        poter = nltk.PorterStemmer()
        tags = [poter.stem(tag).lower() for tag in tags.split(',')]

        for i, tfile in enumerate(tfiles):
            print '[%d], %s, %s' % (i, tfile.title, tfile.path)
            print [(tag, tfile.tags.get(tag, 0)) for tag in tags]
            print 

    @classmethod
    def returnAdobePath(cls):
        import win32api
        import win32con
        import re
        
        key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE,r'SOFTWARE\Classes\Applications\AcroRD32.exe\shell\Read\command',0,win32con.KEY_READ)
        adobe_reader = win32api.RegQueryValue(key, '')
        
        key = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE,r'SOFTWARE\Classes\Applications\Acrobat.exe\shell\Open\command',0,win32con.KEY_READ)
        adobe_acrobat = win32api.RegQueryValue(key, '')

        return (re.findall(r'"(.+?)"', adobe_reader)[0], re.findall(r'"(.+?)"', adobe_acrobat)[0])

    @classmethod
    def openPdf(cls, pdf_path):
        def _openpdf(pdf_path):
            try:
                import subprocess
                subprocess.Popen("%s %s" % (cls.pdf_reader_path, pdf_path))
            except Exception, e:
                print 'open pdf failed', e

        if not cls.pdf_reader_path:
            (adobe_reader_path, adobe_acrobat_path) = cls.returnAdobePath()
            if adobe_acrobat_path:
                cls.pdf_reader_path = adobe_acrobat_path
                _openpdf(pdf_path)
                return
            
            if adobe_reader_path:
                cls.pdf_reader_path = adobe_reader_path
                _openpdf(pdf_path)
                return

            print 'input the path to the software to open pdf documents. it can be Adobe Reader, Adobe Acrobat, etc. e.g: "c:\\program fils\\adobe\\acrobat.exe". Input "x" to skip'
            input = raw_input()
            if input == 'x':
                return
            else:
                cls.pdf_reader_path = input
                _openpdf(pdf_path)
                return

        else:
            _openpdf(pdf_path)
            return
            

    @classmethod
    def run(cls):
        Tagging.initiate()

        while(1):
            print """
            1: generate tags of a folder
            2: search for the tfiles containing certain tags
            x: exit
            """
            input = raw_input()
            if(input == 'x'):
                break
            elif(input == '1'): #update tags of a folder
                print 'input the path of the folder to which to generate tags:'
                path = raw_input() #get path
                Tagging.tagFolder(path)
            elif(input == '2'): #search
                print 'input the tag to search for. (E.g: tag1,tag2):'
                tags = raw_input()
                tfiles = Tagging.searchTags(tags)
                if not tfiles:
                    print 'no matched tfile found'

                else:
                    while(1):
                        Tagging.displayFiles(tags, tfiles)

                        print
                        print 'input the index of the file (%d to %d) to open. input "x" to return' % (0, len(tfiles))

                        input = raw_input()
                        if(input == 'x'):
                            break

                        i = 0
                        try:
                            i = int(input)
                        except:
                            print 'wrong input, should be "x" or a number'
                            continue

                        if i> len(tfiles):
                            print 'wrong index: too large'
                        else:
                            Tagging.openPdf(tfiles[i].path)

Tagging.run()
